package gui

import parserjc.Compile
import parserjc.Evaluator
import scala.collection.mutable.ArrayBuffer
import swing._
import scala.swing.Dialog._
import event._
import javax.swing.JEditorPane
import javax.swing.JOptionPane
import javax.swing.UIManager
import java.io.File
import java.util.ArrayList
import java.util.HashSet
import javax.swing.event.HyperlinkEvent
import javax.swing.event.HyperlinkListener
import javax.swing.text.html.HTMLDocument
import javax.swing.text.html.HTMLFrameHyperlinkEvent
import lang.LOC
import actors._
import action._
import actors.Actor._
import parser._

/**
 * Diese Klasse zeichnet die oberste Ebene des sichtbaren Programms. Die
 * Grundstruktur sieht zwei SplitPanes vor. Zum einen wird das Fenster in
 * eine rechte und linke Seite aufgeteilt. Zusätzlich wird die rechte Seite
 * nochmals in oben und unten getrennt. Es existieren also insgesamt drei
 * Frames, die - von einer Mindestgröße abgesehen - vom Nutzer nach eigenem
 * Ermessen in der Größe geändert werden können.
 */

case class Layout extends Component {
  
  var openprojects:ArrayBuffer[ScaProject] = new ArrayBuffer[ScaProject]
  
  UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  val menuBar:CMenuBar = new CMenuBar(this)

  var toolbar = new ToolBar(32)
  toolbar.addButton("new", MenuFile(LOC.get("menu1item1"), this, 1),
                    LOC.get("menu1item1"),LOC.get("menu1item1"))
  toolbar.addButton("open_2", MenuFile(LOC.get("menu1item2"), this, 2),
                    LOC.get("menu1item2"), LOC.get("menu1item2"))
  toolbar.addButton("save", MenuFile(LOC.get("menu1item4"), this, 4),
                    LOC.get("menu1item4"), LOC.get("menu1item4"))
  toolbar.addSeparator
  toolbar.addButton("play_ausgabe", MenuVerify(LOC.get("menu3item1"), this, 1),
                    LOC.get("menu3item1"), LOC.get("menu3item1"))
  toolbar.addButton("play_trace", MenuVerify(LOC.get("menu3item2"), this, 2),
                    LOC.get("menu3item2"), LOC.get("menu3item2"))
  toolbar.addButton("play_propagate", MenuVerify(LOC.get("menu3item3"), this, 3),
                    LOC.get("menu3item3"), LOC.get("menu3item3"))
  toolbar.addButton("play_prove", MenuVerify(LOC.get("menu3item4"), this, 4),
                    LOC.get("menu3item4"), LOC.get("menu3item4"))
  toolbar.addSeparator
  toolbar.addButton("openext", MenuFile(LOC.get("menu1item8"), this, 8),
                    LOC.get("menu1item8"), LOC.get("menu1item8"))
  
  var left = new ScrollTreePane(this)
  
  //Fenster fuer Tabs
  var tabstop = new TabbedPaneClosable(this)
  var tabsbot = new TabbedPaneNormal()
  
  //Tabs
  var tabsbot_pane1 = new ScrollTextAreaPane(false)
  var tabsbot_pane2 = new ScrollTextAreaPane(false)
  var tabsbot_pane3 = new ScrollTextAreaPane(false)
  var tabsbot_pane4 = new ScrollTextAreaPane(false)
  tabsbot_pane1.setEditorKit(HighlighterAusgabe.getEditorKit, "text/ausgabe")
  tabsbot_pane2.setEditorKit(HighlighterAusgabe.getEditorKit, "text/ausgabe")
  tabsbot_pane3.setEditorKit(HighlighterAusgabe.getEditorKit, "text/ausgabe")
  tabsbot_pane4.setEditorKit(HighlighterAusgabe.getEditorKit, "text/ausgabe")

  tabsbot.addTab(LOC.get("Ausgabe"), tabsbot_pane1, "ausgabe_16.png")
  tabsbot.addTab(LOC.get("Trace"), tabsbot_pane2, "trace_16.png")
  tabsbot.addTab(LOC.get("Propagiert"), tabsbot_pane3, "propagate_16.png")
  tabsbot.addTab(LOC.get("Beweisbedarf"), tabsbot_pane4, "prove_16.png")
  
  var rightSide = new SplitPane(Orientation.Horizontal) {
    dividerLocation = java.awt.Toolkit.getDefaultToolkit().getScreenSize().height / 2
    dividerSize = 7
    
    tabstop.addTab(LOC.get("Startseite"), Layout.startuptab, "icon_16.png")
    //tabstop.addTab("TEST",Layout.testtab,"icon_16.png")
    tabstop.setSelectedIndex(0)
    topComponent = tabstop
    topComponent.minimumSize = new Dimension(0,200)    
    //topComponent.preferredSize = new Dimension(0,800)
    bottomComponent = tabsbot
    bottomComponent.minimumSize = new Dimension(0,200)
  }
  
  var statusBar = new CustomStatusBar
  
  /**
   * Inhalt des Hauptfensters. Es ist als SplitPane definiert, da die
   * grundsätzliche Anordnung zwei vertikal getrennte Spalten sind.
   */  
  var layout_top = new SplitPane(Orientation.Vertical) {
    dividerLocation = 350
    dividerSize = 7
    
    leftComponent = left
    leftComponent.minimumSize = new Dimension(100,0) // TODO wieder ändern
    rightComponent = rightSide
  }
  var layout = new BorderPanel() {
    add(toolbar, BorderPanel.Position.North)
    add(layout_top, BorderPanel.Position.Center)
    add(statusBar, BorderPanel.Position.South)
  }
  
  /**
   * Getter und Setter
   * fuer Text aus ScrollTextAreaPane aus Top-Tabs
   */
  def getTabTopText(i:Int): String = {
    var tc = tabstop.getComponent(i) match {
      case c:ScrollTextAreaPane => c
      case _ => null
    }
    if(tc  != null)
      return tc.getText()
    return ""
  }
  
  def getSelectedTabTopText(): String = getTabTopText(tabstop.peer.getSelectedIndex)
  
  def setTabTopText(i:Int,a:String): Unit = {
    var tc = tabstop.getComponent(i) match {
      case c:ScrollTextAreaPane => c
      case _ => null
    }
    if(tc  != null)
      tc.setText(a)
  }
  
  def setSelectedTabTopText(i:String): Unit = setTabTopText(tabstop.peer.getSelectedIndex,i)
  
  /**
   * Getter und Setter
   * fuer Text aus ScrollTextAreaPane aus Bottom-Tabs
   */
  def getTabBotText(i:Int): String = {
    var tc = tabsbot.getComponent(i) match {
      case c:ScrollTextAreaPane => c
      case _ => null
    }
    if(tc  != null)
      return tc.getText()
    return ""
  }
  
  def getSelectedTabBotText(): String = getTabBotText(tabsbot.peer.getSelectedIndex)
  
  def setTabBotText(i:Int,a:String): Unit = {
    var tc = tabsbot.getComponent(i) match {
      case c:ScrollTextAreaPane => c
      case _ => null
    }
    if(tc  != null)
      tc.setText(a)
  }
  
  def setSelectedTabBotText(i:String): Unit = setTabBotText(tabsbot.peer.getSelectedIndex,i)
  
  /**
   * Tab-Verwaltung(hinzufuegen/entfernen)
   * Top und Bottom
   */
  def addTabTop(title:String, comp:Component): Unit = {
    tabstop.addTab(title,comp)
  }
  
  def removeTabTop(i:Int) = {
    tabstop.removeTab(i)
  }
  
  def addTabBot(title:String, comp:Component): Unit = {
    tabsbot.addTab(title,comp)
  }
  
  def removeTabBot(i:Int) = {
    tabsbot.removeTab(i)
  }
  
  /*
   * Projektverwaltung
   */
  def newProject(name: String, datei: File,
                 tabpane: MyTabbedPane, textpane: ScrollTextAreaPane) = {
    var comp = new Compile()
    var inp = ""
    if(datei != null)
      if(datei.exists)
        inp = scala.io.Source.fromFile(datei).mkString
    comp.setInput(toEvalCase(inp.replaceAll("\n"," ")))
    comp.run()
    var eval = new Evaluator(comp.getSICTree)
    eval.getEWU()
    var zielproc = MenuVerify(null,this,0).getTargetProc(eval)
    var usertestfilestring:String = null
    var testfile:File = null
    if(zielproc != null) {
      //Test-File-Abfrage
      var aktordner:File = datei.getParentFile
      var aktpath = aktordner.getAbsolutePath
      var anderedateien = aktordner.list
      var testdateien = new ArrayList[String]()
      for(i <- anderedateien) {
        var splitted = i.split("_")
        if(splitted.length >= 3) {
          if(splitted(0).toLowerCase.equals("test") && 
             splitted(1).toLowerCase.equals(eval.hauptprozedurname.toLowerCase) &&
             splitted(2).toLowerCase.equals(zielproc.toLowerCase)
          ) {
            testdateien.add(i)
          }
        } 
      }

      if(testdateien.size == 0) {
        Dialog.showMessage(this, LOC.get("FehlerKeineTestDatei"), LOC.get("Fehler"),
                           Dialog.Message.Info)
      }
      else {
        usertestfilestring = JOptionPane.showInputDialog(this.peer, 
                                   LOC.get("TestDateiAuswahlText") + ":",
                                   LOC.get("TestDateiAuswahl"), 
                                   JOptionPane.QUESTION_MESSAGE, null,
                                   testdateien.toArray, null) match {
          case x:String => x
          case _ => null
        }
      }
      if(usertestfilestring != null) {
        usertestfilestring = datei.getParentFile + "\\" + usertestfilestring
        testfile = new File(usertestfilestring)
      }   
    }
      
    openprojects.append(ScaProject(name, datei, tabpane, textpane, zielproc, testfile))
  }
  
  /*
   * Schliesst das uebergebene Projekt
   */
  def closeProject(project: ScaProject) : Unit = {
    if(project != null) {
      if(project.edited == true) {
        var r = Dialog.showConfirmation(this, LOC.get("DateiSpeichern"),
                                        LOC.get("DateiSpeichernTitel"),
                                        Dialog.Options.YesNo, Dialog.Message.Question)
        if(r.equals(Result.Yes))
          project.saveToFile(this)
      }
      removeTabTop(project.getTabIndex)
      for(i <- 0 to openprojects.length-1) {
        if(openprojects(i).equals(project)) {
          openprojects.remove(i)
          exit;
        }
      }
      // Neues aktives Projekt ermitteln, falls vorhanden untere Tabs updaten
      var nactiv = getActivProject
      if(nactiv != null) {
        tabsbot_pane1.setText(nactiv.t1)
        tabsbot_pane2.setText(nactiv.t2)
        tabsbot_pane3.setText(nactiv.t3)
        tabsbot_pane4.setText(nactiv.t4)
      }
      else {
        tabsbot_pane1.setText("")
        tabsbot_pane2.setText("")
        tabsbot_pane3.setText("")
        tabsbot_pane4.setText("")
      }
      
    }
  }
  
  /*
   * Gibt das aktuell im Vordergrund befindliche
   * Projekt zurueck.
   */
  def getActivProject() : ScaProject = {
    var ret:ScaProject = null
    if(openprojects.length >= 1 && tabstop.peer.getTabCount > 0) 
    {
      var i = tabstop.peer.getSelectedIndex
      var tc = tabstop.getComponent(i)
    
      for(j <- 0 to openprojects.length-1) {
        if(openprojects(j).textpane.equals(tc))
          ret = openprojects(j)
      }
    } 
    return ret
  }
  
  /*
   * setzt das aktuell im Vordergrund befindliche
   * Projekt.
   */
  def setActivProject(project: ScaProject) = {
    tabstop.setSelectedIndex(project.getTabIndex)
    //Untere Tabs updaten
    tabsbot_pane1.setText(project.t1)
    tabsbot_pane2.setText(project.t2)
    tabsbot_pane3.setText(project.t3)
    tabsbot_pane4.setText(project.t4)
  }
  
  /*
   * Gibt falls vorhanden das zum Tab i gehoerende Projekt
   * zurueck.
   */
  def getProjectAt(i:Int) : ScaProject = {
    var ret:ScaProject = null
    for(j <- 0 to openprojects.length-1) {
      if(openprojects(j).getTabIndex == i)
        ret = openprojects(j)
    }
    return ret
  }
  
  /*
   * Gibt falls vorhanden das ScaProject zum uebergebenen
   * File zurueck
   */
  def getProjectWithFile(file:File) : ScaProject = {
    var ret:ScaProject = null
    for(j <- 0 to openprojects.length-1) {
      if(openprojects(j).maindatei != null)
        if(openprojects(j).maindatei.equals(file))
          ret = openprojects(j)
    }
    return ret
  }
  
  def hasNonsavedProjects() : Boolean = {
    var ret = false
    for(j <- 0 to openprojects.length-1) {
      if(openprojects(j).edited)
        ret = true
    }
    return ret
  }
  
  def getFocusedTextArea() : EditorPane = {
    return tabstop.getSelectedTextArea
  }
  
  def toEvalCase(str: String) : String = {
    var ret = ""
    var lines = str.split("\n")
    var artmp:HashSet[String] = new HashSet()
    artmp.addAll(CONST.KEYWORDS(Layout.prolamode))
    artmp.add("integer")
    artmp.add("first")
    artmp.add("last")
    artmp.add("forall")
    artmp.add("exists")
    var ar = artmp.toArray

    for(i <- 0 to lines.length-1) {
      var out = lines(i).split(" ")
      lines(i) = ""
      for(k <- 0 to out.length-1) {
        for(j <- 0 to ar.size-1) {
          var pos = out(k).toLowerCase.indexOf(ar(j).toString)
          if(pos == 0) {
            if(out(k).length > ar(j).toString.length) {
              if(out(k).substring(ar(j).toString.length,
                                  ar(j).toString.length+1).matches("""\W"""))
                out(k) = out(k).replaceAll("(?i)" + ar(j).toString,ar(j).toString)
            }
            else
              out(k) = out(k).replaceAll("(?i)" + ar(j).toString,ar(j).toString)
          } 
          else if(pos > 0 && out(k).substring(pos-1, pos).matches("""\W""")) {
            if(out(k).substring(pos, out(k).length-1).length > ar(j).toString.length) {
              if(out(k).substring(ar(j).toString.length + pos,
                                  ar(j).toString.length+1 + pos).matches("""\W"""))
                out(k) = out(k).replaceAll("(?i)" + ar(j).toString,ar(j).toString)
            }
            else
              out(k) = out(k).replaceAll("(?i)" + ar(j).toString,ar(j).toString)
          }
        }
        lines(i) += out(k)
        if(k < out.length-1)
          lines(i) += " "
      }
      
      ret += lines(i)
      if(i < lines.length-1)
        ret += "\n"
    }
    return ret
  }
}

object Layout {
  var prolamode = 0
  var currentlang = "de"
  var tracesteps = 100
  var printsize = 10
  var tmps = new ScrollTextAreaPane(false)
  var startuptab = new BorderPanel(){
    peer.setBorder(tmps.peer.getBorder)
    var url = getClass().getResource("doc/" + LOC.currentlang + "/index.html")
    var textarea = new EditorPane(){
      editable = false
      peer.setPage(url)
      
      peer.addHyperlinkListener(new HyperlinkListener{
          def hyperlinkUpdate(e: HyperlinkEvent) : Unit = {
            if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
              var pane =  e.getSource().asInstanceOf[JEditorPane];
              if (e.isInstanceOf[HTMLFrameHyperlinkEvent]) {
                var evt = e.asInstanceOf[HTMLFrameHyperlinkEvent];
                var doc = pane.getDocument().asInstanceOf[HTMLDocument];
                doc.processHTMLFrameHyperlinkEvent(evt);
              } else {
                try {
                  pane.setPage(e.getURL());
                } catch {
                  case t: Throwable => t.printStackTrace();
                }
              }
            }
          }
        })
    }
    add(textarea, BorderPanel.Position.Center) 
  }  
}


 